package me.chanjar.weixin.mp.api.impl;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import me.chanjar.weixin.common.bean.ToJson;
import me.chanjar.weixin.common.bean.WxJsapiSignature;
import me.chanjar.weixin.common.bean.WxNetCheckResult;
import me.chanjar.weixin.common.enums.TicketType;
import me.chanjar.weixin.common.enums.WxType;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.service.WxImgProcService;
import me.chanjar.weixin.common.service.WxOAuth2Service;
import me.chanjar.weixin.common.service.WxOcrService;
import me.chanjar.weixin.common.session.StandardSessionManager;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.common.util.DataUtils;
import me.chanjar.weixin.common.util.RandomUtils;
import me.chanjar.weixin.common.util.crypto.SHA1;
import me.chanjar.weixin.common.util.http.*;
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
import me.chanjar.weixin.mp.api.*;
import me.chanjar.weixin.mp.bean.WxMpSemanticQuery;
import me.chanjar.weixin.mp.bean.result.WxMpCurrentAutoReplyInfo;
import me.chanjar.weixin.mp.bean.result.WxMpSemanticQueryResult;
import me.chanjar.weixin.mp.bean.result.WxMpShortKeyResult;
import me.chanjar.weixin.mp.config.WxMpConfigStorage;
import me.chanjar.weixin.mp.enums.WxComponentApiUrl;
import me.chanjar.weixin.mp.enums.WxMpApiUrl;
import me.chanjar.weixin.mp.util.WxMpConfigStorageHolder;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public abstract class AbstractComponentWxMpServiceImpl<H, P> implements WxMpService, RequestHttp<H, P> {
    private static final Logger log = LoggerFactory.getLogger(AbstractComponentWxMpServiceImpl.class);
    private static final JsonParser JSON_PARSER = new JsonParser();
    protected WxSessionManager sessionManager = new StandardSessionManager();
    protected Lock componentTokenLock = new ReentrantLock();
    private WxMpKefuService kefuService = new WxMpKefuServiceImpl(this);
    private WxMpMaterialService materialService = new WxMpMaterialServiceImpl(this);
    private WxMpMenuService menuService = new WxMpMenuServiceImpl(this);
    private WxMpUserService userService = new WxMpUserServiceImpl(this);
    private WxMpUserTagService tagService = new WxMpUserTagServiceImpl(this);
    private WxMpQrcodeService qrCodeService = new WxMpQrcodeServiceImpl(this);
    private WxMpCardService cardService = new WxMpCardServiceImpl(this);
    private WxMpStoreService storeService = new WxMpStoreServiceImpl(this);
    private WxMpDataCubeService dataCubeService = new WxMpDataCubeServiceImpl(this);
    private WxMpUserBlacklistService blackListService = new WxMpUserBlacklistServiceImpl(this);
    private WxMpTemplateMsgService templateMsgService = new WxMpTemplateMsgServiceImpl(this);
    private WxMpSubscribeMsgService subscribeMsgService = new WxMpSubscribeMsgServiceImpl(this);
    private WxMpDeviceService deviceService = new WxMpDeviceServiceImpl(this);
    private WxMpShakeService shakeService = new WxMpShakeServiceImpl(this);
    private WxMpMemberCardService memberCardService = new WxMpMemberCardServiceImpl(this);
    private WxMpMassMessageService massMessageService = new WxMpMassMessageServiceImpl(this);
    private WxMpAiOpenService aiOpenService = new WxMpAiOpenServiceImpl(this);
    private WxMpWifiService wifiService = new WxMpWifiServiceImpl(this);
    private WxMpMarketingService marketingService = new WxMpMarketingServiceImpl(this);
    private WxMpCommentService commentService = new WxMpCommentServiceImpl(this);
    private WxOcrService ocrService = new WxMpOcrServiceImpl(this);
    private WxImgProcService imgProcService = new WxMpImgProcServiceImpl(this);
    private Map<String, WxMpConfigStorage> configStorageMap;
    private int retrySleepMillis = 1000;
    private int maxRetryTimes = 5;


    private WxOAuth2Service wxOAuth2Service;
    private WxMpGuideService wxMpGuideService;
    private WxMpGuideBuyerService wxMpGuideBuyerService;
    private WxMpGuideTagService wxMpGuideTagService;
    private WxMpGuideMaterialService wxMpGuideMaterialService;
    private WxMpGuideMassedJobService wxMpGuideMassedJobService;
    private WxMpMerchantInvoiceService wxMpMerchantInvoiceService;
    private WxMpDraftService wxMpDraftService;
    private WxMpFreePublishService wxMpFreePublishService;


    public AbstractComponentWxMpServiceImpl() {
        //this.wxMpMerchantInvoiceService = new WxMpllerchantInvoiceServicelmpl(this, this.cardService);
        this.wxMpGuideService = new WxMpGuideServiceImpl( this);
       // this.quideBuyerService = new WxlpGuideBuyerServiceImpl( this);
        this.wxMpGuideTagService = new WxMpGuideTagServiceImpl( this);
        this.wxMpGuideMassedJobService = new WxMpGuideMassedJobServiceImpl( this);
        this.wxMpGuideMaterialService = new WxMpGuideMaterialServiceImpl( this);
        this.wxOAuth2Service = new WxMpComponentOAuth2ServiceImpl(  this);
        //this.reimburseInvoiceService = new WxMpReimburseInvoiceServiceImpl( this);
        this.wxMpDraftService = new WxMpDraftServiceImpl(  this);
        this.wxMpFreePublishService = new WxMpFreePublishServiceImpl(  this);
    }

    abstract String getComponentAccessToken();

    @Override
    public boolean checkSignature(String timestamp, String nonce, String signature) {
        try {
            return SHA1.gen(new String[]{this.getWxMpConfigStorage().getToken(), timestamp, nonce}).equals(signature);
        } catch (Exception var5) {
            log.error("Checking signature failed, and the reason is :" + var5.getMessage());
            return false;
        }
    }

    @Override
    public String getTicket(TicketType type) throws WxErrorException {
        return this.getTicket(type, false);
    }

    @Override
    public String getTicket(TicketType type, boolean forceRefresh) throws WxErrorException {
        Lock lock = this.getWxMpConfigStorage().getTicketLock(type);

        lock.lock();
        try {
            if (forceRefresh) {
                this.getWxMpConfigStorage().expireTicket(type);
            }

            if (this.getWxMpConfigStorage().isTicketExpired(type)) {
                String responseContent = (String) this.execute(SimpleGetRequestExecutor.create(this), (String) (WxComponentApiUrl.Other.GET_TICKET_URL.getUrl(this.getWxMpConfigStorage()) + type.getCode()), null);
                JsonObject tmpJsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject();
                String jsapiTicket = tmpJsonObject.get("ticket").getAsString();
                int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt();
                this.getWxMpConfigStorage().updateTicket(type, jsapiTicket, expiresInSeconds);
            }
        } finally {
            lock.unlock();
        }

        return this.getWxMpConfigStorage().getTicket(type);
    }

    @Override
    public String getJsapiTicket() throws WxErrorException {
        return this.getJsapiTicket(false);
    }

    @Override
    public String getJsapiTicket(boolean forceRefresh) throws WxErrorException {
        return this.getTicket(TicketType.JSAPI, forceRefresh);
    }

    @Override
    public WxJsapiSignature createJsapiSignature(String url) throws WxErrorException {
        long timestamp = System.currentTimeMillis() / 1000L;
        String randomStr = RandomUtils.getRandomStr();
        String jsapiTicket = this.getJsapiTicket(false);
        String signature = SHA1.genWithAmple(new String[]{"jsapi_ticket=" + jsapiTicket, "noncestr=" + randomStr, "timestamp=" + timestamp, "url=" + url});
        WxJsapiSignature jsapiSignature = new WxJsapiSignature();
        jsapiSignature.setAppId(this.getWxMpConfigStorage().getAppId());
        jsapiSignature.setTimestamp(timestamp);
        jsapiSignature.setNonceStr(randomStr);
        jsapiSignature.setUrl(url);
        jsapiSignature.setSignature(signature);
        return jsapiSignature;
    }

    @Override
    public String getAccessToken() throws WxErrorException {
        return this.getAccessToken(false);
    }

    @Override
    public String shortUrl(String longUrl) throws WxErrorException {
        if (longUrl.contains("&access_token=")) {
            throw new WxErrorException(WxError.builder().errorCode(-1).errorMsg("要转换的网址中存在非法字符｛&access_token=｝，会导致微信接口报错，属于微信bug，请调整地址，否则不建议使用此方法！").build());
        } else {
            JsonObject o = new JsonObject();
            o.addProperty("action", "long2short");
            o.addProperty("long_url", longUrl);
            String responseContent = this.post((WxComponentApiUrl) WxComponentApiUrl.Other.SHORTURL_API_URL, o.toString());
            JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent);
            return tmpJsonElement.getAsJsonObject().get("short_url").getAsString();
        }
    }

    @Override
    public WxMpSemanticQueryResult semanticQuery(WxMpSemanticQuery semanticQuery) throws WxErrorException {
        String responseContent = this.post((WxComponentApiUrl) WxComponentApiUrl.Other.SEMANTIC_SEMPROXY_SEARCH_URL, semanticQuery.toJson());
        return WxMpSemanticQueryResult.fromJson(responseContent);
    }

//    @Override
//    public String oauth2buildAuthorizationUrl(String redirectURI, String scope, String state) {
//        return String.format(WxComponentApiUrl.Other.CONNECT_OAUTH2_AUTHORIZE_URL.getUrl(this.getWxMpConfigStorage()), this.getWxMpConfigStorage().getAppId(), URIUtil.encodeURIComponent(redirectURI), scope, StringUtils.trimToEmpty(state));
//    }

    @Override
    public String buildQrConnectUrl(String redirectURI, String scope, String state) {
        return String.format(WxComponentApiUrl.Other.QRCONNECT_URL.getUrl(this.getWxMpConfigStorage()), this.getWxMpConfigStorage().getAppId(), URIUtil.encodeURIComponent(redirectURI), scope, StringUtils.trimToEmpty(state));
    }

//    private WxMpOAuth2AccessToken getOAuth2AccessToken(String url) throws WxErrorException {
//        try {
//            RequestExecutor<String, String> executor = SimpleGetRequestExecutor.create(this);
//            String responseText = (String) executor.execute(url, null, WxType.MP);
//            return WxMpOAuth2AccessToken.fromJson(responseText);
//        } catch (IOException var4) {
//            throw new RuntimeException(var4);
//        }
//    }
//
//    public WxMpOAuth2AccessToken oauth2getAccessToken() throws WxErrorException {
//        return oauth2getAccessToken();
//    }

//    @Override
//    public WxMpOAuth2AccessToken oauth2getAccessToken(String code) throws WxErrorException {
//        String componentAppid, accessToken;
//        WxMpConfigStorage config = this.getWxMpConfigStorage();
//        Jedis jedis = ((ComponentWxMpRedisConfigImpl) config).getJedisPool().getResource();
//        componentAppid = jedis.get(Constant.COMPONENT_APPID);
//        log.info("componentAppid:" + componentAppid);
//
//        accessToken = this.getComponentAccessToken();
//        log.info("accessToken:" + accessToken);
//        String url = String.format(WxComponentApiUrl.Component.OAUTH2_ACCESS_TOKEN_URL.getUrl(this.getWxMpConfigStorage()), this.getWxMpConfigStorage().getAppId(), code, componentAppid, accessToken);
//        jedis.close();
//        return this.getOAuth2AccessToken(url);
//    }

//    @Override
//    public WxMpOAuth2AccessToken oauth2refreshAccessToken(String refreshToken) throws WxErrorException {
//        String url = String.format(WxComponentApiUrl.Other.OAUTH2_REFRESH_TOKEN_URL.getUrl(this.getWxMpConfigStorage()), this.getWxMpConfigStorage().getAppId(), refreshToken);
//        return this.getOAuth2AccessToken(url);
//    }

//    @Override
//    public WxMpUser oauth2getUserInfo(WxMpOAuth2AccessToken token, String lang) throws WxErrorException {
//        if (lang == null) {
//            lang = "zh_CN";
//        }
//
//        String url = String.format(WxComponentApiUrl.Component.OAUTH2_USERINFO_URL.getUrl(this.getWxMpConfigStorage()), token.getAccessToken(), token.getOpenId(), lang);
//
//        try {
//            RequestExecutor<String, String> executor = SimpleGetRequestExecutor.create(this);
//            String responseText = (String) executor.execute(url, null, WxType.MP);
//            return WxMpUser.fromJson(responseText);
//        } catch (IOException var6) {
//            throw new RuntimeException(var6);
//        }
//    }

//    @Override
//    public boolean oauth2validateAccessToken(WxMpOAuth2AccessToken token) {
//        String url = String.format(WxComponentApiUrl.Other.OAUTH2_VALIDATE_TOKEN_URL.getUrl(this.getWxMpConfigStorage()), token.getAccessToken(), token.getOpenId());
//
//        try {
//            SimpleGetRequestExecutor.create(this).execute(url, null, WxType.MP);
//            return true;
//        } catch (IOException var4) {
//            throw new RuntimeException(var4);
//        } catch (WxErrorException var5) {
//            return false;
//        }
//    }

    @Override
    public String[] getCallbackIP() throws WxErrorException {
        String responseContent = this.get((WxComponentApiUrl) WxComponentApiUrl.Other.GET_CALLBACK_IP_URL, (String) null);
        JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent);
        JsonArray ipList = tmpJsonElement.getAsJsonObject().get("ip_list").getAsJsonArray();
        String[] ipArray = new String[ipList.size()];

        for (int i = 0; i < ipList.size(); ++i) {
            ipArray[i] = ipList.get(i).getAsString();
        }

        return ipArray;
    }

    @Override
    public WxNetCheckResult netCheck(String action, String operator) throws WxErrorException {
        JsonObject o = new JsonObject();
        o.addProperty("action", action);
        o.addProperty("check_operator", operator);
        String responseContent = this.post((WxComponentApiUrl) WxComponentApiUrl.Other.NETCHECK_URL, o.toString());
        return WxNetCheckResult.fromJson(responseContent);
    }

    @Override
    public WxMpCurrentAutoReplyInfo getCurrentAutoReplyInfo() throws WxErrorException {
        return WxMpCurrentAutoReplyInfo.fromJson(this.get((WxComponentApiUrl) WxComponentApiUrl.Other.GET_CURRENT_AUTOREPLY_INFO_URL, (String) null));
    }

    @Override
    public void clearQuota(String appid) throws WxErrorException {
        JsonObject o = new JsonObject();
        o.addProperty("appid", appid);
        this.post((WxComponentApiUrl) WxComponentApiUrl.Other.CLEAR_QUOTA_URL, o.toString());
    }

    @Override
    public String get(String url, String queryParam) throws WxErrorException {
        return (String) this.execute(SimpleGetRequestExecutor.create(this), (String) url, queryParam);
    }

    @Override
    public String get(WxMpApiUrl url, String queryParam) throws WxErrorException {
        return this.get(url.getUrl(this.getWxMpConfigStorage()), queryParam);
    }

    public String get(WxComponentApiUrl url, String queryParam) throws WxErrorException {
        return this.get(url.getUrl(this.getWxMpConfigStorage()), queryParam);
    }

    @Override
    public String post(String url, String postData) throws WxErrorException {
        return (String) this.execute(SimplePostRequestExecutor.create(this), (String) url, postData);
    }

    @Override
    public String post(WxMpApiUrl url, String postData) throws WxErrorException {
        return this.post(url.getUrl(this.getWxMpConfigStorage()), postData);
    }

    @Override
    public <T, E> T execute(RequestExecutor<T, E> executor, WxMpApiUrl url, E data) throws WxErrorException {
        return this.execute(executor, url.getUrl(this.getWxMpConfigStorage()), data);
    }

    /**
     * 第三方平台调用post请求
     *
     * @param url
     * @param postData
     * @param isComponent
     * @return
     * @throws WxErrorException
     */
    public String post(String url, String postData, boolean isComponent) throws WxErrorException {
        return (String) this.execute(SimplePostRequestExecutor.create(this), (String) url, postData, isComponent);
    }

    /**
     * 第三方平台调用post请求
     */
    public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data, boolean isComponent) throws WxErrorException {
        int retryTimes = 0;

        while (true) {
            try {
                return this.executeInternal(executor, uri, data, isComponent);
            } catch (WxErrorException var10) {
                if (retryTimes + 1 > this.maxRetryTimes) {
                    log.warn("重试达到最大次数【{}】", this.maxRetryTimes);
                    throw new RuntimeException("微信服务端异常，超出重试次数");
                }

                WxError error = var10.getError();
                if (error.getErrorCode() != -1) {
                    throw var10;
                }

                int sleepMillis = this.retrySleepMillis * (1 << retryTimes);

                try {
                    log.warn("微信系统繁忙，{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1);
                    Thread.sleep((long) sleepMillis);
                } catch (InterruptedException var9) {
                    throw new RuntimeException(var9);
                }

                if (retryTimes++ >= this.maxRetryTimes) {
                    log.warn("重试达到最大次数【{}】", this.maxRetryTimes);
                    throw new RuntimeException("微信服务端异常，超出重试次数");
                }
            }
        }
    }

    protected <T, E> T executeInternal(RequestExecutor<T, E> executor, String uriWithAccessToken, E data, boolean isComponent) throws WxErrorException {
        E dataForLog = DataUtils.handleDataWithSecret(data);

        try {
            T result = executor.execute(uriWithAccessToken, data, WxType.MP);
            log.debug("\n【请求地址】: {}\n【请求参数】：{}\n【响应数据】：{}", new Object[]{uriWithAccessToken, dataForLog, result});
            return result;
        } catch (WxErrorException var9) {
            WxError error = var9.getError();
            if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001 || error.getErrorCode() == 40014) {
                this.getWxMpConfigStorage().expireAccessToken();
                if (this.getWxMpConfigStorage().autoRefreshToken()) {
                    return this.execute(executor, uriWithAccessToken, data, isComponent);
                }
            }

            if (error.getErrorCode() != 0) {
                log.error("\n【请求地址】: {}\n【请求参数】：{}\n【错误信息】：{}", new Object[]{uriWithAccessToken, dataForLog, error});
                throw new WxErrorException(error, var9);
            } else {
                return null;
            }
        } catch (IOException var10) {
            log.error("\n【请求地址】: {}\n【请求参数】：{}\n【异常信息】：{}", new Object[]{uriWithAccessToken, dataForLog, var10.getMessage()});
            throw new WxErrorException(WxError.builder().errorMsg(var10.getMessage()).build(), var10);
        }
    }


    public String post(WxComponentApiUrl url, String postData) throws WxErrorException {
        return this.post(url.getUrl(this.getWxMpConfigStorage()), postData);
    }

    public <T, E> T execute(RequestExecutor<T, E> executor, WxComponentApiUrl url, E data) throws WxErrorException {
        return this.execute(executor, url.getUrl(this.getWxMpConfigStorage()), data);
    }

    @Override
    public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException {
        int retryTimes = 0;

        while (true) {
            try {
                return this.executeInternal(executor, uri, data);
            } catch (WxErrorException var10) {
                if (retryTimes + 1 > this.maxRetryTimes) {
                    log.warn("重试达到最大次数【{}】", this.maxRetryTimes);
                    throw new RuntimeException("微信服务端异常，超出重试次数");
                }

                WxError error = var10.getError();
                if (error.getErrorCode() != -1) {
                    throw var10;
                }

                int sleepMillis = this.retrySleepMillis * (1 << retryTimes);

                try {
                    log.warn("微信系统繁忙，{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1);
                    Thread.sleep((long) sleepMillis);
                } catch (InterruptedException var9) {
                    throw new RuntimeException(var9);
                }

                if (retryTimes++ >= this.maxRetryTimes) {
                    log.warn("重试达到最大次数【{}】", this.maxRetryTimes);
                    throw new RuntimeException("微信服务端异常，超出重试次数");
                }
            }
        }
    }

    protected <T, E> T executeInternal(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException {
        E dataForLog = DataUtils.handleDataWithSecret(data);
        if (uri.contains("access_token=")) {
            throw new IllegalArgumentException("uri参数中不允许有access_token: " + uri);
        } else {
            String accessToken = this.getAccessToken(false);//this.getComponentAccessToken();
            String uriWithAccessToken = uri + (uri.contains("?") ? "&" : "?") + "access_token=" + accessToken;

            try {
                T result = executor.execute(uriWithAccessToken, data, WxType.MP);
                log.debug("\n【请求地址】: {}\n【请求参数】：{}\n【响应数据】：{}", new Object[]{uriWithAccessToken, dataForLog, result});
                return result;
            } catch (WxErrorException var9) {
                WxError error = var9.getError();
                if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001 || error.getErrorCode() == 40014) {
                    this.getWxMpConfigStorage().expireAccessToken();
                    if (this.getWxMpConfigStorage().autoRefreshToken()) {
                        return this.execute(executor, uri, data);
                    }
                }

                if (error.getErrorCode() != 0) {
                    log.error("\n【请求地址】: {}\n【请求参数】：{}\n【错误信息】：{}", new Object[]{uriWithAccessToken, dataForLog, error});
                    throw new WxErrorException(error, var9);
                } else {
                    return null;
                }
            } catch (IOException var10) {
                log.error("\n【请求地址】: {}\n【请求参数】：{}\n【异常信息】：{}", new Object[]{uriWithAccessToken, dataForLog, var10.getMessage()});
                throw new WxErrorException(WxError.builder().errorMsg(var10.getMessage()).build(), var10);
            }
        }
    }

    @Override
    public WxMpConfigStorage getWxMpConfigStorage() {
        return this.configStorageMap.size() == 1 ? (WxMpConfigStorage) this.configStorageMap.values().iterator().next() : (WxMpConfigStorage) this.configStorageMap.get(WxMpConfigStorageHolder.get());
    }

    @Override
    public void setWxMpConfigStorage(WxMpConfigStorage wxConfigProvider) {
        String defaultMpId = WxMpConfigStorageHolder.get();
        this.setMultiConfigStorages(ImmutableMap.of(defaultMpId, wxConfigProvider), defaultMpId);
    }

    @Override
    public void setMultiConfigStorages(Map<String, WxMpConfigStorage> configStorages) {
        this.setMultiConfigStorages(configStorages, (String) configStorages.keySet().iterator().next());
    }

    @Override
    public void setMultiConfigStorages(Map<String, WxMpConfigStorage> configStorages, String defaultMpId) {
        this.configStorageMap = Maps.newHashMap(configStorages);
        WxMpConfigStorageHolder.set(defaultMpId);
        this.initHttp();
    }

    @Override
    public void addConfigStorage(String mpId, WxMpConfigStorage configStorages) {
        synchronized (this) {
            if (this.configStorageMap.containsKey(mpId)) {
                throw new RuntimeException("该公众号标识已存在，请更换其他标识！");
            } else {
                this.configStorageMap.put(mpId, configStorages);
            }
        }
    }

    @Override
    public void removeConfigStorage(String mpId) {
        synchronized (this) {
            this.configStorageMap.remove(mpId);
        }
    }

    @Override
    public WxMpService switchoverTo(String mpId) {
        if (this.configStorageMap.containsKey(mpId)) {
            WxMpConfigStorageHolder.set(mpId);
            return this;
        } else {
            throw new RuntimeException(String.format("无法找到对应【%s】的公众号配置信息，请核实！", mpId));
        }
    }

    @Override
    public boolean switchover(String mpId) {
        if (this.configStorageMap.containsKey(mpId)) {
            WxMpConfigStorageHolder.set(mpId);
            return true;
        } else {
            log.error("无法找到对应【{}】的公众号配置信息，请核实！", mpId);
            return false;
        }
    }

    @Override
    public void setRetrySleepMillis(int retrySleepMillis) {
        this.retrySleepMillis = retrySleepMillis;
    }

    @Override
    public void setMaxRetryTimes(int maxRetryTimes) {
        this.maxRetryTimes = maxRetryTimes;
    }

    @Override
    public WxMpKefuService getKefuService() {
        return this.kefuService;
    }

    @Override
    public WxMpMaterialService getMaterialService() {
        return this.materialService;
    }

    @Override
    public WxMpMenuService getMenuService() {
        return this.menuService;
    }

    @Override
    public WxMpUserService getUserService() {
        return this.userService;
    }

    @Override
    public WxMpUserTagService getUserTagService() {
        return this.tagService;
    }

    @Override
    public WxMpQrcodeService getQrcodeService() {
        return this.qrCodeService;
    }

    @Override
    public WxMpCardService getCardService() {
        return this.cardService;
    }

    @Override
    public WxMpDataCubeService getDataCubeService() {
        return this.dataCubeService;
    }

    @Override
    public WxMpUserBlacklistService getBlackListService() {
        return this.blackListService;
    }

    @Override
    public WxMpStoreService getStoreService() {
        return this.storeService;
    }

    @Override
    public WxMpTemplateMsgService getTemplateMsgService() {
        return this.templateMsgService;
    }

    @Override
    public WxMpSubscribeMsgService getSubscribeMsgService() {
        return this.subscribeMsgService;
    }

    @Override
    public WxMpDeviceService getDeviceService() {
        return this.deviceService;
    }

    @Override
    public WxMpShakeService getShakeService() {
        return this.shakeService;
    }

    @Override
    public WxMpMemberCardService getMemberCardService() {
        return this.memberCardService;
    }

    @Override
    public RequestHttp getRequestHttp() {
        return this;
    }

    @Override
    public WxMpMassMessageService getMassMessageService() {
        return this.massMessageService;
    }

    @Override
    public void setKefuService(WxMpKefuService kefuService) {
        this.kefuService = kefuService;
    }

    @Override
    public void setMaterialService(WxMpMaterialService materialService) {
        this.materialService = materialService;
    }

    @Override
    public void setMenuService(WxMpMenuService menuService) {
        this.menuService = menuService;
    }

    @Override
    public void setUserService(WxMpUserService userService) {
        this.userService = userService;
    }

//    @Override
//    public void setTagService(WxMpUserTagService tagService) {
//        this.tagService = tagService;
//    }

//    @Override
//    public void setQrCodeService(WxMpQrcodeService qrCodeService) {
//        this.qrCodeService = qrCodeService;
//    }

    @Override
    public void setCardService(WxMpCardService cardService) {
        this.cardService = cardService;
    }

    @Override
    public void setStoreService(WxMpStoreService storeService) {
        this.storeService = storeService;
    }

    @Override
    public void setDataCubeService(WxMpDataCubeService dataCubeService) {
        this.dataCubeService = dataCubeService;
    }

    @Override
    public void setBlackListService(WxMpUserBlacklistService blackListService) {
        this.blackListService = blackListService;
    }

    @Override
    public void setTemplateMsgService(WxMpTemplateMsgService templateMsgService) {
        this.templateMsgService = templateMsgService;
    }

    @Override
    public void setDeviceService(WxMpDeviceService deviceService) {
        this.deviceService = deviceService;
    }

    @Override
    public void setShakeService(WxMpShakeService shakeService) {
        this.shakeService = shakeService;
    }

    @Override
    public void setMemberCardService(WxMpMemberCardService memberCardService) {
        this.memberCardService = memberCardService;
    }

    @Override
    public void setMassMessageService(WxMpMassMessageService massMessageService) {
        this.massMessageService = massMessageService;
    }

    @Override
    public WxMpAiOpenService getAiOpenService() {
        return this.aiOpenService;
    }

    @Override
    public void setAiOpenService(WxMpAiOpenService aiOpenService) {
        this.aiOpenService = aiOpenService;
    }

    @Override
    public WxMpWifiService getWifiService() {
        return this.wifiService;
    }

    @Override
    public WxOcrService getOcrService() {
        return this.ocrService;
    }

    @Override
    public WxMpMarketingService getMarketingService() {
        return this.marketingService;
    }

    @Override
    public void setMarketingService(WxMpMarketingService marketingService) {
        this.marketingService = marketingService;
    }

    @Override
    public void setOcrService(WxOcrService ocrService) {
        this.ocrService = ocrService;
    }

    @Override
    public WxMpCommentService getCommentService() {
        return this.commentService;
    }

    @Override
    public void setCommentService(WxMpCommentService commentService) {
        this.commentService = commentService;
    }

    @Override
    public WxImgProcService getImgProcService() {
        return this.imgProcService;
    }

    @Override
    public String genShorten(String s, Integer integer) throws WxErrorException {
        return null;
    }

    @Override
    public WxMpShortKeyResult fetchShorten(String s) throws WxErrorException {
        return null;
    }

    @Override
    public String post(WxMpApiUrl wxMpApiUrl, Object o) throws WxErrorException {
        return null;
    }

    @Override
    public String post(WxMpApiUrl wxMpApiUrl, JsonObject jsonObject) throws WxErrorException {
        return null;
    }

    @Override
    public WxMpReimburseInvoiceService getReimburseInvoiceService() {
        return null;
    }

    @Override
    public WxMpDraftService getDraftService() {
        return null;
    }

    @Override
    public WxMpFreePublishService getFreePublishService() {
        return null;
    }

    @Override
    public void setReimburseInvoiceService(WxMpReimburseInvoiceService wxMpReimburseInvoiceService) {

    }

    @Override
    public void setUserTagService(WxMpUserTagService wxMpUserTagService) {
        this.tagService=wxMpUserTagService;
    }

    @Override
    public void setQrcodeService(WxMpQrcodeService wxMpQrcodeService) {
        this.qrCodeService=wxMpQrcodeService;
    }

    @Override
    public void setImgProcService(WxImgProcService wxImgProcService) {
        this.imgProcService=wxImgProcService;
    }

    @Override
    public WxOAuth2Service getOAuth2Service() {
        return this.wxOAuth2Service;
    }

    @Override
    public void setOAuth2Service(WxOAuth2Service wxOAuth2Service) {
        this.wxOAuth2Service=wxOAuth2Service;
    }

    @Override
    public WxMpGuideService getGuideService() {
        return this.wxMpGuideService;
    }

    @Override
    public void setGuideService(WxMpGuideService wxMpGuideService) {
        this.wxMpGuideService=wxMpGuideService;
    }


    @Override
    public WxMpGuideBuyerService getGuideBuyerService() {
        return this.wxMpGuideBuyerService;
    }

    @Override
    public void setGuideBuyerService(WxMpGuideBuyerService wxMpGuideBuyerService) {
        this.wxMpGuideBuyerService =wxMpGuideBuyerService;
    }

    @Override
    public WxMpGuideTagService getGuideTagService() {
        return this.wxMpGuideTagService;
    }

    @Override
    public void setGuideTagService(WxMpGuideTagService wxMpGuideTagService) {
        this.wxMpGuideTagService =wxMpGuideTagService;
    }

    @Override
    public WxMpGuideMaterialService getGuideMaterialService() {
        return this.wxMpGuideMaterialService;
    }

    @Override
    public void setGuideMaterialService(WxMpGuideMaterialService wxMpGuideMaterialService) {
        this.wxMpGuideMaterialService=wxMpGuideMaterialService;
    }

    @Override
    public WxMpGuideMassedJobService getGuideMassedJobService() {
        return this.wxMpGuideMassedJobService;
    }

    @Override
    public void setGuideMassedJobService(WxMpGuideMassedJobService wxMpGuideMassedJobService) {
        this.wxMpGuideMassedJobService=wxMpGuideMassedJobService;
    }

    @Override
    public WxMpMerchantInvoiceService getMerchantInvoiceService() {
        return this.wxMpMerchantInvoiceService;
    }

    @Override
    public void setMerchantInvoiceService(WxMpMerchantInvoiceService wxMpMerchantInvoiceService) {
        this.wxMpMerchantInvoiceService=wxMpMerchantInvoiceService;
    }

    @Override
    public void setDraftService(WxMpDraftService wxMpDraftService) {
        this.wxMpDraftService=wxMpDraftService;
    }

    @Override
    public void setFreePublishService(WxMpFreePublishService wxMpFreePublishService) {
        this.wxMpFreePublishService =wxMpFreePublishService;
    }

    @Override
    public String post(String url, Object obj) throws WxErrorException {
        return this.execute(SimplePostRequestExecutor.create(this),url, WxGsonBuilder.create().toJson(obj));
    }

    @Override
    public String post(String url, JsonObject jsonObject) throws WxErrorException {
        return this.post(url, jsonObject.toString());
    }

    @Override
    public String post(String url, ToJson obj) throws WxErrorException {
        return this.post(url, obj.toJson());
    }


}
